<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>视力迷宫</title>
<style>
  @import url('https://fonts.googleapis.com/css2?family=Orbitron&display=swap');
  body {
    margin: 0; padding: 0;
    background: linear-gradient(135deg, #1f1c2c, #928dab);
    font-family: 'Orbitron', sans-serif;
    color: #eee;
    display: flex; flex-direction: column; align-items: center;
    user-select: none;
  }
  h1 {
    margin: 20px 0 10px;
    letter-spacing: 2px;
    text-shadow: 0 0 10px #7fffd4;
  }
  #info {
    margin-bottom: 10px;
    font-size: 18px;
    text-shadow: 0 0 5px #4dd0e1;
  }
  #info > span {
    margin: 0 15px;
  }
  #canvas-container {
    border: 6px solid #4dd0e1;
    border-radius: 12px;
    box-shadow: 0 0 30px #00ffe7aa;
    background: #0b0c10;
    max-width: 800px;
    max-height: 800px;
    overflow: auto;
  }
  canvas {
    display: block;
    background: #0b0c10;
  }
  #controls {
    margin: 15px 0;
  }
  button {
    background: #00bfa5;
    border: none;
    padding: 10px 20px;
    border-radius: 8px;
    font-weight: bold;
    font-size: 16px;
    color: #012;
    cursor: pointer;
    transition: background-color 0.3s ease;
    box-shadow: 0 0 15px #00bfa5cc;
    margin: 0 10px;
  }
  button:hover:not(:disabled) {
    background: #00e6c9;
  }
  button:disabled {
    background: #555;
    cursor: default;
    box-shadow: none;
  }
  #footer {
    font-size: 14px;
    color: #aaa;
    margin-bottom: 20px;
  }
</style>
</head>
<body>
<h1>Eller算法完美迷宫（三关）</h1>
<div id="info">
  <span>关卡: <span id="level">1</span>/3</span>
  <span>当前关时间: <span id="time">0.0</span> 秒</span>
  <span>失败次数: <span id="fail">0</span></span>
  <span>总计时: <span id="totalTime">0.0</span> 秒</span>
</div>
<div id="canvas-container">
  <canvas id="mazeCanvas"></canvas>
</div>
<div id="controls">
  <button id="restartBtn">重新开始当前关</button>
</div>
<div id="footer">用方向键/WASD移动，撞墙失败次数+1，抵达红色终点自动进入下一关。</div>

<script>
(() => {
  const LEVELS = [
    { rows: 10, cols: 10 },
    { rows: 50, cols: 50 },
    { rows: 100, cols: 100 }
  ];

  const mazeCanvas = document.getElementById('mazeCanvas');
  const ctx = mazeCanvas.getContext('2d');
  const levelDisplay = document.getElementById('level');
  const timeDisplay = document.getElementById('time');
  const failDisplay = document.getElementById('fail');
  const totalTimeDisplay = document.getElementById('totalTime');
  const restartBtn = document.getElementById('restartBtn');

  let currentLevelIndex = 0;
  let rows = 0, cols = 0;
  let gridHeight = 0, gridWidth = 0;
  let cellSize = 20;

  let maze = [];
  let playerPos = {x:1, y:1};
  let startPos = {x:1, y:1};
  let endPos = {x:0, y:0};
  let failCount = 0;

  let timerId = null;
  let startTime = null;

  let totalTimerId = null;
  let totalStartTime = null;
  let totalElapsed = 0; // 累计总时间

  function generateMazeWithEller(rows, cols) {
    const width = cols;
    const height = rows;

    let sets = new Array(width).fill(0).map((_, i) => i + 1);
    let horizontalWalls = Array(height - 1).fill(null).map(() => Array(width).fill(true));
    let verticalWalls = Array(height).fill(null).map(() => Array(width -1).fill(true));

    for(let row = 0; row < height; row++) {
      for(let col = 0; col < width - 1; col++) {
        if (sets[col] !== sets[col + 1]) {
          if (Math.random() < 0.5 || row === height -1) {
            verticalWalls[row][col] = false;
            let oldSet = sets[col + 1];
            let newSet = sets[col];
            for(let k = 0; k < width; k++) {
              if(sets[k] === oldSet) sets[k] = newSet;
            }
          }
        }
      }

      if(row === height - 1) break;

      horizontalWalls[row].fill(true);
      let setMembers = {};
      for(let col = 0; col < width; col++) {
        if(!setMembers[sets[col]]) setMembers[sets[col]] = [];
        setMembers[sets[col]].push(col);
      }
      for(let setId in setMembers) {
        let members = setMembers[setId];
        let holes = [];
        let holeCount = 1 + Math.floor(Math.random() * members.length);
        let shuffled = members.slice().sort(() => Math.random() - 0.5);
        for(let i = 0; i < holeCount; i++) {
          holes.push(shuffled[i]);
        }
        for(let col of holes) {
          horizontalWalls[row][col] = false;
        }
      }

      let newSets = new Array(width).fill(0);
      let nextSetId = Math.max(...sets) + 1;
      for(let col = 0; col < width; col++) {
        if(horizontalWalls[row][col]) {
          newSets[col] = nextSetId++;
        } else {
          newSets[col] = sets[col];
        }
      }
      sets = newSets;
    }

    let mazeHeight = height * 2 + 1;
    let mazeWidth = width * 2 + 1;
    let maze = Array(mazeHeight).fill(null).map(() => Array(mazeWidth).fill(1));

    for(let r = 0; r < height; r++) {
      for(let c = 0; c < width; c++) {
        maze[r*2+1][c*2+1] = 0;
      }
    }
    for(let r = 0; r < height; r++) {
      for(let c = 0; c < width -1; c++) {
        if(!verticalWalls[r][c]) {
          maze[r*2+1][c*2+2] = 0;
        }
      }
    }
    for(let r = 0; r < height -1; r++) {
      for(let c = 0; c < width; c++) {
        if(!horizontalWalls[r][c]) {
          maze[r*2+2][c*2+1] = 0;
        }
      }
    }
    return maze;
  }

  function drawMaze() {
    ctx.clearRect(0, 0, mazeCanvas.width, mazeCanvas.height);
    for(let x=0; x<gridHeight; x++) {
      for(let y=0; y<gridWidth; y++) {
        if(maze[x][y] === 1) {
          ctx.fillStyle = '#111';
          ctx.shadowColor = 'black';
          ctx.shadowBlur = 2;
        } else {
          ctx.fillStyle = '#eee';
          ctx.shadowColor = '#b6fffe';
          ctx.shadowBlur = 4;
        }
        ctx.fillRect(y * cellSize, x * cellSize, cellSize, cellSize);
      }
    }
    ctx.fillStyle = '#00ff00';
    ctx.shadowColor = '#00ff00';
    ctx.shadowBlur = 15;
    ctx.fillRect(startPos.y * cellSize, startPos.x * cellSize, cellSize, cellSize);
    ctx.fillStyle = '#ff3b3b';
    ctx.shadowColor = '#ff3b3b';
    ctx.shadowBlur = 15;
    ctx.fillRect(endPos.y * cellSize, endPos.x * cellSize, cellSize, cellSize);
  }

  function drawPlayer() {
    const cx = playerPos.y * cellSize + cellSize/2;
    const cy = playerPos.x * cellSize + cellSize/2;
    const r = cellSize * 0.35;
    ctx.beginPath();
    ctx.fillStyle = '#1de9b6';
    ctx.shadowColor = '#1de9b6';
    ctx.shadowBlur = 20;
    ctx.arc(cx, cy, r, 0, Math.PI * 2);
    ctx.fill();
  }

  function render() {
    drawMaze();
    drawPlayer();
  }

  function movePlayer(dx, dy) {
    if(timerId === null) return;
    let nx = playerPos.x + dx;
    let ny = playerPos.y + dy;

    if(nx < 0 || nx >= gridHeight || ny < 0 || ny >= gridWidth || maze[nx][ny] === 1) {
      failCount++;
      failDisplay.textContent = failCount;
      return;
    }

    playerPos.x = nx;
    playerPos.y = ny;
    render();

    if(playerPos.x === endPos.x && playerPos.y === endPos.y) {
      clearInterval(timerId);
      timerId = null;
      // 累加本关用时
      let now = performance.now();
      totalElapsed += (now - startTime);

      setTimeout(() => {
        alert(`恭喜通过第${currentLevelIndex+1}关！用时${timeDisplay.textContent}秒，失败${failCount}次。`);
        if(currentLevelIndex + 1 < LEVELS.length) {
          startLevel(currentLevelIndex + 1);
        } else {
          alert('已完成所有关卡！游戏结束。');
          restartBtn.disabled = true;
          // 结束总计时器
          if(totalTimerId) {
            clearInterval(totalTimerId);
            totalTimerId = null;
          }
        }
      }, 100);
    }
  }

  function startTimer() {
    startTime = performance.now();
    if(timerId) clearInterval(timerId);
    timerId = setInterval(() => {
      let now = performance.now();
      timeDisplay.textContent = ((now - startTime)/1000).toFixed(1);
    }, 100);
  }

  function startTotalTimer() {
    totalStartTime = performance.now();
    if(totalTimerId) clearInterval(totalTimerId);
    totalTimerId = setInterval(() => {
      let now = performance.now();
      totalTimeDisplay.textContent = ((totalElapsed + (now - totalStartTime)) / 1000).toFixed(1);
    }, 100);
  }

  restartBtn.addEventListener('click', () => {
    failCount = 0;
    failDisplay.textContent = failCount;
    timeDisplay.textContent = '0.0';
    playerPos = {...startPos};
    render();
    startTimer();
    restartBtn.disabled = false;
  });

  window.addEventListener('keydown', e => {
    if(restartBtn.disabled) return;
    const key = e.key.toLowerCase();
    if(key === 'arrowup' || key === 'w') {
      movePlayer(-1, 0);
      e.preventDefault();
    } else if(key === 'arrowdown' || key === 's') {
      movePlayer(1, 0);
      e.preventDefault();
    } else if(key === 'arrowleft' || key === 'a') {
      movePlayer(0, -1);
      e.preventDefault();
    } else if(key === 'arrowright' || key === 'd') {
      movePlayer(0, 1);
      e.preventDefault();
    }
  });

  function startLevel(index) {
    currentLevelIndex = index;
    let lvl = LEVELS[index];
    rows = lvl.rows;
    cols = lvl.cols;
    gridHeight = rows * 2 + 1;
    gridWidth = cols * 2 + 1;

    failCount = 0;
    failDisplay.textContent = failCount;
    timeDisplay.textContent = '0.0';
    levelDisplay.textContent = index + 1;

    restartBtn.disabled = false;

    const maxCanvasSize = 800;
    let maxDimension = Math.max(gridHeight, gridWidth);
    cellSize = Math.floor(maxCanvasSize / maxDimension);
    if(cellSize < 3) cellSize = 3;

    mazeCanvas.width = gridWidth * cellSize;
    mazeCanvas.height = gridHeight * cellSize;

    maze = generateMazeWithEller(rows, cols);
    startPos = {x: 1, y: 1};
    endPos = {x: gridHeight - 2, y: gridWidth - 2};
    playerPos = {...startPos};
    render();
    startTimer();
    // 开始总计时器，只第一关开始
    if(currentLevelIndex === 0) {
      totalElapsed = 0;
      startTotalTimer();
    }
  }

  startLevel(0);

})();
</script>
</body>
</html>
